from collections.abc import Callable
from typing import Any,List
from django.core.handlers.wsgi import WSGIRequest
from django.http.request import HttpRequest
from rest_framework.request import Request
    
class attribute:
    key= ""
    value= None
    
    def __init__(self,k,v="") -> None:
        self.key=k.upper()
        self.value=v
        
    @property
    def exists(self):
        return True if self.value else False

class oProperty(property):
    k= ''
    def __init__(self,k, fget: Callable[[Any], Any] | None = None, fset: Callable[[Any, Any], None] | None = None, fdel: Callable[[Any], None] | None = None, doc: str | None = None) -> None:
        self.k= k
        fget= fget if fget else self.getter
        fset= fset if fset else self.setter
        fdel= fdel if fdel else self.deleter
        doc= doc if doc else k
        super().__init__(fget, fset, fdel, doc)
        
    def setter(self,__instance, __value) -> property:
        o=getattr(__instance,self.k)
        setattr(o,"value",__value)
    
    def getter(self, __instance) -> property:
        return getattr(__instance,self.k)
    
    def deleter(self, __fdel: Callable[[Any], None]) -> property:
        return super().deleter(__fdel)

class Meta(type):
    def __new__(cls, name, bases, dct):
        attrs={
            "k_map":{}
        }
        for k,v in dct.items():
            attrs[k]=v
            if isinstance(v,str):
                if not ["__module__","__qualname__"].__contains__(k):
                    local_var= "".join(["__",k.lower(),"__"])
                    attrs[local_var]=attribute(v)
                    attrs.get("k_map").update({v:k})
                    attrs[k]=oProperty(local_var)
        return super(Meta,cls).__new__(cls, name, bases, attrs)

class interface_token(metaclass=Meta):
    rq_context= {}
    
    def __init__(self,rq_context:WSGIRequest|HttpRequest|Request= None):
        if rq_context:
            if isinstance(rq_context,(WSGIRequest,HttpRequest,Request)):
                self.rq_context= rq_context
                for k,v in self.k_map.items():
                    if k in self.rq_context.META:
                        setattr(self,v,self.rq_context.META(k))
            else:
                raise TypeError("rq_context type error.")
        
    def load(self,rq_context:WSGIRequest|HttpRequest|Request):
        if isinstance(rq_context,(WSGIRequest,HttpRequest,Request)):
            self.rq_context= rq_context
            for k,v in self.k_map.items():
                if k in self.rq_context.META:
                    setattr(self,v,self.rq_context.META[k])
                else:
                    setattr(self,v,None)
        else:
            raise TypeError("rq_context type error.")     
          
    def generate_headers(self):
        context={"headers":{}}
        for k,v in self.k_map.items():
            o= self.__getattribute__(v)
            if o.value:
                context['headers'].update({o.key:o.value})
        return context
    
    def override_context_headers(self):
        if self.rq_context:
            for k,v in self.k_map.items():
                o= self.__getattribute__(v)
                if o.value:
                    self.rq_context.META[k]=o.value
        else:
            raise TypeError("request context is none.")

    def get_token_value(self,k:attribute):
        if k.key in self.k_map:
            return self.__getattribute__(self.k_map[k.key]).value
        else:
            return None
        
    def set_token_value(self,k:attribute,v):
        if k.key in self.k_map:
            setattr(self,self.k_map[k.key], v)
        else:
            raise KeyError("not found key.")
        
class REQUEST_MANAGER:
    @classmethod
    def setHeader(cls, key:'attribute|str', value:any=None, request:WSGIRequest|HttpRequest|Request=None, override= True):
        if request:
            if isinstance(key,str):
                if key not in request.META:
                    request.META[key]=value
                else:
                    if override:
                        request.META[key]= value
            elif isinstance(key,attribute):
                if key.key not in request.META:
                    request.META[key.key]=key.value
                else:
                    if override:
                        request.META[key.key]= key.value
            return request
        else:
            raise ValueError("request is none.")
    @classmethod
    def getHeader(cls, key:'attribute|str', request, default_v=None, original= False):
        if isinstance(key,str):
            if not original:
                key=key.replace("-","_").upper()
                if not key.startswith("HTTP_"):
                    key="".join(["HTTP_",key])
            if key not in request.META:
                if not default_v:
                    raise ValueError("HTTP HEADER not found '%s"%(key))
                else:
                    return default_v
            else:
                return request.META[key]
        elif isinstance(key,attribute):
            if key.key not in request.META:
                if not default_v:
                    raise ValueError("HTTP HEADER not found '%s"%(key.key))
                else:
                    return default_v
            else:
                return request.META[key.key]
    @classmethod
    def headerExists(cls,k:'str|List[attribute]',context,original= False):
        if isinstance(k,str):
            if not original:
                key=k.replace("-","_").upper()
                if not key.startswith("HTTP_"):
                    key="".join(["HTTP_",key])
            return True if key in context.META else False    
        elif isinstance(k,list):
            try:
                for o in k:
                    if isinstance(key,str):
                        if not original:
                            key=o.replace("-","_").upper()
                            if not key.startswith("HTTP_"):
                                key="".join(["HTTP_",key])
                        if key not in context.META:
                            return False
                    elif isinstance(o,attribute):
                        if o.key not in context.META:
                            return False
                    else:
                        raise TypeError("%s type error str or list[attribute]"%o)
            except Exception as e:
                raise e
        else:
            raise ValueError("k type error, str or list.")
    @classmethod
    def getHeaders(cls,keys:List[dict|str],request):
        result={}
        for o in keys:
            if isinstance(o,dict):
                result[o['key']]= cls.getHeader(o["key"],request,o["default_v"])
            else:
                result[o]= cls.getHeader(o,request)
        return result
    @classmethod
    def getQueryParams(cls, keys:List[dict|str],request):
        result={}
        for o in keys:
            if isinstance(o,dict):
                if o["key"] not in request.query_params:
                    if "default_v" not in o:
                        raise ValueError('HTTP REQUEST QUERY PARAMS not found "%s"'%(o))
                    else:
                        result[o["key"]]=o["default_v"]
                else:
                    result[o["key"]]=request.query_params[o["key"]]
            else:
                if o not in request.query_params:
                    raise ValueError('HTTP REQUEST QUERY PARAMS not found "%s"'%(o))
                result[o]= request.query_params[o]
        return result
    @classmethod
    def getQueryParam(cls, key:str,request,default_v=None):
        if key not in request.query_params:
            if not default_v:
                raise ValueError("HTTP REQUEST QUERY PARAMETER NOT FOUND '%s'"%(key))
            else:
                return default_v
        return request.query_params[key]
    @classmethod
    def getDataParam(cls,key:str,rq:WSGIRequest|HttpRequest|Request, default_v= None):
        if key not in rq.data:
            if default_v:
                return default_v
            else:
                raise ValueError("HTTP REQUEST DATA PARAMETER NOT FOUND '%s'"%(key))
        else:
            return rq.data[key]
    @classmethod
    def getDataParams(cls, keys:List[dict|str],rq:WSGIRequest|HttpRequest|Request):
        result={}
        for o in keys:
            if isinstance(o,dict):
                if o["key"] not in rq.data:
                    if "default_v" not in o:
                        raise ValueError('HTTP REQUEST DATA PARAMS not found "%s"'%(o))
                    else:
                        result[o["key"]]=o["default_v"]
                else:
                    result[o["key"]]=rq.data[o["key"]]
            else:
                if o not in rq.data:
                    raise ValueError('HTTP REQUEST DATA PARAMS not found "%s"'%(o))
                result[o]= rq.data[o]
        return result
    @classmethod
    def getRemoteAddr(cls,request):
        if "REMOTE_ADDR" not in request.META:
            return "0.0.0.0"
        else:
            return request.META["REMOTE_ADDR"]